<?php


namespace server\impl;


use common\core\server\BaseServer;
use common\helpers\BaseHelper;
use server\dao\Devices;
use server\dao\DevicesProcess;
use server\dao\EquComputer;
use server\dao\EquLane;
use server\dao\EquLine;
use server\dao\EquStation;
use server\dao\LaneDevices;
use server\dao\Lic;
use server\dao\States;
use server\EquipmentInterface;
use server\LogInterface;
use Yii;
use yii\helpers\ArrayHelper;
use yii\mongodb\Exception;

class EquipmentImpl extends BaseServer implements EquipmentInterface
{
    protected $line;
    protected $station;
    protected $lane;
    protected $computer;
    protected $devices;
    protected $lane_devices;
    protected $log;
    protected $states;
    protected $lic;
    protected $devices_process;

    public function __construct(LogInterface $log, array $config = [])
    {
        $this->line = new EquLine();
        $this->station = new EquStation();
        $this->lane = new EquLane();
        $this->computer = new EquComputer();
        $this->devices = new Devices();
        $this->lane_devices = new LaneDevices();
        $this->states = new States();
        $this->lic = new Lic();
        $this->devices_process = new  DevicesProcess();
        $this->log = $log;
        parent::__construct($config);
    }


    /**
     * @return array|\yii\db\ActiveRecord[]
     */
    public function lineList()
    {
        $line_list = $this->line->getLineList();
        if ($line_list) {
            $station_list = $this->station->getStationList();
            if ($station_list) {
                $lane = $this->lane->getLaneList();
                $computer = $this->lane_devices->getAllInfo();
                foreach ($line_list as &$line_item) {
                    foreach ($station_list as &$station_item) {
                        foreach ($lane as $lane_item) {
                            foreach ($computer as $computer_item) {
                                if ($computer_item['line_id'] == $line_item['id'] && $computer_item['station_id'] == $station_item['id'] && $computer_item['lane_id'] == $lane_item['id']) {
                                    $lane_item['computer'][] = $computer_item;
                                }
                            }
                            if ($lane_item['station_id'] == $station_item['id'] && $station_item['line_id'] == $line_item['id']) {
                                $station_item['lane'][] = $lane_item;
                            }
                        }
                        if ($station_item['line_id'] == $line_item['id']) {
                            $line_item['station'][] = $station_item;
                            // continue;

                        }

                    }
                }
            }
        }
        return $line_list;
    }

    public function updateLine(array $data)
    {
        $line = $this->line->getLineByName($data['name']);
        if ($line) {
            $this->invalidParamException('线路{{' . $data['name'] . '}}名称已存在');
        }
        return $this->line->updateByParams($data);
    }

    public function stationList(array $data)
    {
        $list = $this->station->getStationList($data);
        $results = $this->htmlSpecialChars($list, [
            'line_name'
        ], [
            'line_name' => function ($data) {
                $info = $this->line->getById($data['line_id']);
                return $info ? $info['name'] : '';
            }
        ]);
        return $results;
    }

    public function updateStation(array $data)
    {
        $station = $this->station->getStationByName($data['name']);
        if ($station) {
            $this->invalidParamException('站点{{' . $data['name'] . '}}名称已存在');
        }
        return $this->station->updateByParams($data);

    }

    public function laneList(array $data)
    {
        $list = $this->lane->getLaneList($data);
        $results = $this->htmlSpecialChars($list, [
            'station_name'
        ], [
            'station_name' => function ($data) {
                $info = $this->station->getById($data['station_id']);
                return $info ? $info['name'] : '';
            }
        ]);
        return $results;
    }

    public function updateLane(array $data)
    {
        $lane = $this->lane->getLaneByName($data['name']);
        if ($lane) {
            $this->invalidParamException('车道{{' . $data['name'] . '}}名称已存在');
        }
        return $this->lane->updateByParams($data);
    }

    public function computerList(array $data)
    {
        $list = $this->lane_devices->getDevicesList($data);
        $ip_array = [];
        if (!empty($list)) {
            $ip_array = array_column($list, 'ip');
        }
        $ip_data = $this->log->queryLastRuntimeByIps($ip_array);
        if (!empty($ip_data)) {
            $ip_data = array_column($ip_data, NULL, 'uuid');
        }
        $devices_list = $this->devices->getDevicesList();
        if (!empty($devices_list)) {
            $devices_list = array_column($devices_list, NULL, 'uuid');
        }
        $line_list = $this->line->getLineList();
        if (!empty($line_list)) {
            $line_list = array_column($line_list, NULL, 'id');
        }
        $station_list = $this->station->getStationList();
        if (!empty($station_list)) {
            $station_list = array_column($station_list, NULL, 'id');
        }
        $lane_list = $this->lane->getLaneList();
        if (!empty($lane_list)) {
            $lane_list = array_column($lane_list, NULL, 'id');
        }
        $timeout = \Yii::$app->params['timeout'];
        $time = time();
        // $list =  $this->computer->getComputeList($data);
        $results = $this->htmlSpecialChars($list, [
            'onLine',
            'line_name',
            'station_name',
            'lane_name',
            'remote_pc_name',
            'software',
            'hardware',
            'timestamp',
            'cpu',
            'ram',
        ], [
            'onLine' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return true;
                }
                return false;
            },
            'line_name' => function ($data) use ($line_list) {
                return isset($line_list[$data['line_id']]) ? $line_list[$data['line_id']]['name'] : '';
            },
            'station_name' => function ($data) use ($station_list) {
                return isset($station_list[$data['station_id']]) ? $station_list[$data['station_id']]['name'] : '';
            },
            'lane_name' => function ($data) use ($lane_list) {
                return isset($lane_list[$data['lane_id']]) ? $lane_list[$data['lane_id']]['name'] : '';
            },
            'remote_pc_name' => function ($data) use ($devices_list) {
                return isset($devices_list[$data['ip']]) ? $devices_list[$data['ip']]['software']['name'] : '';
            },
            'software' => function ($data) use ($devices_list) {
                return isset($devices_list[$data['ip']]) ? $devices_list[$data['ip']]['software'] : [];
            },
            'hardware' => function ($data) use ($devices_list) {
                return isset($devices_list[$data['ip']]) ? $devices_list[$data['ip']]['hardware'] : [];
            },
            'timestamp' => function ($data) use ($devices_list) {
                return isset($devices_list[$data['ip']]) ? $devices_list[$data['ip']]['timestamp'] : [];
            },

            'cpu' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['cpu']['usage'] : 0;
                }
                return 0;
            },
            'ram' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['ram'] : 0;
                }
                return 0;
            }

        ]);
        return $results;
    }

    public function devices()
    {
        try {
            Yii::$app->mongodb->createCommand()->createCollection('devices');
        } catch (Exception $e) {

        }
        $a = $this->lane_devices->getDevicesList();
        return $a;

    }

    public function addComputer(array $data)
    {
        /*$info = $this->devices->getInfoByIP($data['ip']);
        if (empty($info)) {
            //  $this->invalidRequestException('ip不存在');
        }*/
        $list = $this->lane_devices->getDevicesList();
        $devices_count = count($list);
        $lic = $this->lic->getLic();
        if ($lic) {
            if (time() > strtotime($lic['expired'])) {
                $this->invalidRequestException('Lic认证文件不存在或者已到期，新增设备请联系供应商');
            }

            if ($devices_count > $lic['allowClient']) {
                $this->invalidRequestException('设备链接已超过最大数量，新增设备请联系供应商');
            }
        }

        return $this->lane_devices->insertDevices($data);
        // return $this->computer->updateByParams($data);

    }

    public function updateComputer(array $data)
    {
        /*$info = $this->devices->getInfoByIP($data['ip']);
        if (empty($info)) {
            //  $this->invalidRequestException('ip不存在');
        }*/
        return $this->lane_devices->updateDevices($data);
        // return $this->computer->updateByParams($data);
    }


    public function deleteComputer(array $data)
    {
        return $this->lane_devices->deleteDevices($data);
    }


    public function getLaneDevices()
    {
        $list = $this->lane_devices->getDevicesList();
        $results = $this->htmlSpecialChars($list, [
            'line_name',
            'station_name',
            'lane_name'
        ], [
            'line_name' => function ($data) {
                $info = $this->line->getById($data['line_id']);
                return $info ? $info->name : '';
            },
            'station_name' => function ($data) {
                $info = $this->station->getById($data['station_id']);
                return $info ? $info->name : '';
            },
            'lane_name' => function ($data) {
                $info = $this->lane->getById($data['lane_id']);
                return $info ? $info->name : '';

            }

        ]);
        return $results;
    }

    public function getAllInfo()
    {
        $list = $this->lane_devices->getAllInfo();
        $timeout = \Yii::$app->params['timeout'];
        $time = time();
        $ip_array = [];
        if (!empty($list)) {
            $ip_array = array_column($list, 'ip');
        }
        $ip_data = $this->log->queryLastRuntimeByIps($ip_array);
        if (!empty($ip_data)) {
            $ip_data = array_column($ip_data, NULL, 'uuid');
        }
        $line_list = $this->line->getLineList();
        if (!empty($line_list)) {
            $line_list = array_column($line_list, NULL, 'id');
        }
        $station_list = $this->station->getStationList();
        if (!empty($station_list)) {
            $station_list = array_column($station_list, NULL, 'id');
        }
        $lane_list = $this->lane->getLaneList();
        if (!empty($lane_list)) {
            $lane_list = array_column($lane_list, NULL, 'id');
        }
        $states = $this->states->getSatesList();
        if (!empty($states)) {
            $states = array_column($states, NULL, 'uuid');
        }
        //进程
        $process = $this->devices_process->getAllProcess();
        if (!empty($process)) {
            $process = array_column($process, NULL, 'ip');
        }
        $results = $this->htmlSpecialChars($list, [
            'onLine',
            'line_name',
            'station_name',
            'lane_name',
            'cpu',
            'cpu_temperature',
            'ram',
            'disk',
            'network',
            'process',
            'chartData',
            'module',
            'device_warning',
            'process_warning',
        ], [
            'onLine' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return true;
                }
                return false;
            },
            'line_name' => function ($data) use ($line_list) {
                return isset($line_list[$data['line_id']]) ? $line_list[$data['line_id']]['name'] : '';
            },
            'station_name' => function ($data) use ($station_list) {
                return isset($station_list[$data['station_id']]) ? $station_list[$data['station_id']]['name'] : '';
            },
            'lane_name' => function ($data) use ($lane_list) {
                return isset($lane_list[$data['lane_id']]) ? $lane_list[$data['lane_id']]['name'] : '';
            },
            'process' => function ($data) use ($ip_data) {
                return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['process'] : [];
            },
            'cpu' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['cpu']['usage'] : 0;
                }
                return 0;
            },
            'cpu_temperature' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['cpu']['temperature'] : 0;
                }
                return 0;
            },
            'ram' => function ($data) use ($timeout, $time, $ip_data) {

                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                if ($time - $timestamp < $timeout) {
                    return isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['ram'] : 0;
                }
                return 0;
            },
            'disk' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                $disk_tmp = [];
                if ($time - $timestamp < $timeout) {
                    $disk = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['disk'] : [];
                    if (!empty($disk) && isset($disk['usage']) && !empty($disk['usage'])) {
                        foreach ($disk['usage'] as $item) {
                            $disk_tmp[] = ['name' => isset($item['partition']) ? $item['partition'] : '', 'use' => isset($item['used']) ? $item['used'] : 0];
                        }
                    }
                }
                return $disk_tmp;
            },
            'network' => function ($data) use ($timeout, $time, $ip_data) {
                $timestamp = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['timestamp'] : 0;
                $network_tmp = [];
                if ($time - $timestamp < $timeout) {
                    $nets = isset($ip_data[$data['ip']]) ? $ip_data[$data['ip']]['net'] : [];
                    if (!empty($nets)) {
                        foreach ($nets as $net) {
                            $use = $net['load'] && $net['bandwidth'] ? round($net['load'] / $net['bandwidth'], 2) * 100 : 0;
                            $network_tmp[] = ['name' => $net['name'], 'use' => $use];

                        }
                    }
                }
                return $network_tmp;
            },
            'chartData' => function () {
                return [];
            },
            'device_warning' => function ($data) use ($states) {
                return isset($states[$data['ip']]) ? $states[$data['ip']]['device_warning'] : ['level' => 0, 'message' => ''];
            },
            'module' => function ($data) use ($states) {
                return isset($states[$data['ip']]) ? $states[$data['ip']]['module_warning'] : [];
            },
            'process_warning' => function ($data) use ($process, $ip_data) {
                $current_process = isset($ip_data[$data['ip']]) && isset($ip_data[$data['ip']]['process']) ? $ip_data[$data['ip']]['process'] : [];
                $current_process_name = !empty($current_process) ? array_column($current_process, 'name') : [];
                if (isset($process[$data['ip']]) && $process[$data['ip']]['process']) {
                    return array_values(array_diff($process[$data['ip']]['process'], $current_process_name));
                }
                return [];
            },

        ]);
        foreach ($results as $key => $item) {
            if (!$item['line_name'] || !$item['station_name'] || !$item['lane_name']) {
                unset($results[$key]);
            }
        }
        $onLines = array_column($results, 'onLine');
        array_multisort($onLines, SORT_ASC, $results);
        return $results;
    }

    public function getItemInfoByIP($data)
    {
        $device = $this->lane_devices->getInfoByIP(trim($data['ip']));
        $logModule = $this->log->getLogModule();
        $logModuleMap = array_column($logModule, '_id');
        if ($device) {
            $info = $this->log->queryRuntime(['ip' => $device['ip']], true);
            $last_info = array_shift($info);

            $line = $this->line->getById($device['line_id']);
            $station = $this->station->getById($device['station_id']);
            $lane = $this->lane->getById($device['lane_id']);
            $states = $this->states->getSates($data['ip']);
            //$device_info = $this->devices->getInfoByIP($data['ip']);
            $control_ip = ArrayHelper::getValue($device, 'control_ip', '');
            $process = $this->devices_process->getProcessByIP($data['ip']);
            $device['cup_name'] = ArrayHelper::getValue($last_info, 'hardware.cpu.name', '');
            $device['os'] = ArrayHelper::getValue($last_info, 'software.os', '');
            $ram_volume = 0;
            if (ArrayHelper::getValue($info, 'hardware.ram')) {
                foreach ($last_info['hardware']['ram'] as $volume) {
                    $ram_volume += $volume['size'];
                }
            }
            $device['ram_volume'] = round($ram_volume / 1073741824) . 'G';

            $onLine = false;
            if (isset($last_info['timestamp'])) {
                $timeout = \Yii::$app->params['timeout'];
                $time = time();
                if ($time - $last_info['timestamp'] < $timeout) {
                    $onLine = true;
                }
            }
            $device['onLine'] = $onLine;
            $device['line_name'] = $line->name ?? "";
            $device['station_name'] = $station->name ?? "";
            $device['lane_name'] = $lane->name ?? "";
            $cpu = 0;
            if ($onLine) {
                $cpu = $last_info['cpu']['usage'] ?? 0;
            }
            $device['cpu'] = $cpu;
            $cpu_temperature = 0;
            if ($onLine) {
                $cpu_temperature = $last_info['cpu']['temperature'] ?? 0;
            }
            $device['cpu_temperature'] = $cpu_temperature;
            $ram = 0;
            if ($onLine) {
                $ram = $last_info['ram'] ?? 0;
            }
            $device['ram'] = $ram;
            //disk 多个
            $disk_tmp = [];
            if (!empty($last_info['disk']) && $onLine) {
                if (isset($last_info['disk']['usage']) && !empty($last_info['disk']['usage'])) {
                    foreach ($last_info['disk']['usage'] as $disk) {
                        $disk_tmp[] = ['name' => $disk['partition'], 'use' => $disk['used']];
                    }
                }
            }
            $device['disk'] = $disk_tmp;
            //network 多个
            $network_tmp = [];
            if (!empty($last_info['net']) && $onLine) {
                foreach ($last_info['net'] as $net) {
                    $user = $net['load'] ? round($net['load'] / $net['bandwidth'], 2) * 100 : 0;
                    $network_tmp[] = ['name' => $net['name'], 'use' => $user];

                }
            }
            $device['network'] = $network_tmp;
            $device['process'] = $last_info['process'] ?? [];
            $tmp = [];
            if (!empty($info)) {
                foreach ($info as $item) {
                    $tmp[] = [
                        'timestamp' => $item['timestamp'],
                        'cpu' => isset($item['cpu']) && isset($item['cpu']['usage']) ? $item['cpu']['usage'] : 0,
                        'temperature' => isset($item['cpu']) && isset($item['cpu']['temperature']) ? $item['cpu']['temperature'] : 0,
                        'ram' => $item['ram'],
                    ];
                }
            }
            $device['chartData'] = $tmp;
            $device['module'] = $states['module_warning'] ?? [];
            sort($logModuleMap);
            $device['module_map'] = $logModuleMap ?? [];
            $device['device_warning'] = $states['device_warning'] ?? ['level' => 0, 'message' => ''];
            $current_process_name = !empty($device['process']) ? array_column($device['process'], 'name') : [];
            $device['process_warning'] = [];
            if (!empty($process) && isset($process['process'])) {
                $device['process_warning'] = array_values(array_diff($process['process'], $current_process_name));
            }
            $curl_url = 'http://' . trim($device['control_ip']) . '/api/info/all';
            $device['curl_url'] = $curl_url;
            $device['other_info'] = $curl_url ? BaseHelper::curlGet($curl_url) : [];
            $device['control_map'] = [
                '0' => 'USB电源开关接口',
                '1' => '短按电源按钮',
                '2' => '长按电源按钮',
                '3' => '硬件复位',
                '4' => '磁盘备份',
                '5' => '磁盘恢复'
            ];
            return $device;
        } else {
            return [];
        }
    }

    public function getProcessByIP($data)
    {
        try {
            Yii::$app->mongodb->createCommand()->createCollection('devices_process');
        } catch (Exception $e) {

        }
        $process = $this->devices_process->getProcessByIP($data['ip']);
        $result['check_process'] = $process['process'] ?? [];
        $info = $this->log->queryRuntime(['ip' => $data['ip']], true);
        $last_info = array_shift($info);
        $current_process = $last_info['process'] ?? [];
        $result['current_process'] = [];
        if (!empty($current_process)) {
            $result['current_process'] = array_column($current_process, 'name');
        }
        return $result;

    }

    public function updateDevicesProcess($data)
    {
        return $this->devices_process->updateDevicesProcess($data);
    }


}
